// 版权归 瞄你个汪 所有。使用本代码应遵守相关法律法规和Apache 2.0开源许可要求。
package top.geeke.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import top.geeke.toolkit.http.AppException;
import top.geeke.toolkit.http.Result;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.BindException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import java.util.zip.ZipException;

/**
 * <p>
 * 全局异常处理
 * </p>
 * @author 瞄你个汪
 * @since 2024-05-01 12:00:00
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    private final Map<Integer, HttpStatus> HTTP_STATUS_MAP = new HashMap<Integer, HttpStatus>() {{
        put(401, HttpStatus.UNAUTHORIZED);
        put(403, HttpStatus.FORBIDDEN);
    }};

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = AppException.class)
    public Result<Object> exceptionHandler(AppException e) {
        log.error("业务异常：{}", e.getMessage());
        return Result.error(e.getCode(), e.getMessage());
    }

    /**
     * 处理 form data 方式调用接口校验失败抛出的异常
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(org.springframework.validation.BindException.class)
    public String bindExceptionHandler(HttpServletResponse response, org.springframework.validation.BindException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.toList());
        return collect.toString();
    }

    /**
     * 处理 json 请求体调用接口校验失败抛出的异常
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String methodArgumentNotValidExceptionHandler(HttpServletResponse response, MethodArgumentNotValidException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.toList());
        return collect.toString();
    }

    /**
     * 处理单个参数校验失败抛出的异常
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public String constraintViolationExceptionHandler(HttpServletResponse response, ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        List<String> collect = constraintViolations.stream().map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());
        return collect.toString();
    }

    /**
     * 定义常见的异常提示信息映射表
     */
    private static final Map<Class<? extends Throwable>, String> EXCEPTION_MESSAGES;
    static {
        Map<Class<? extends Throwable>, String> messages = new HashMap<>();
        messages.put(NullPointerException.class, "空指针异常");
        messages.put(ArrayIndexOutOfBoundsException.class, "数组越界异常");
        messages.put(SerializationException.class, "序列化异常");
        messages.put(ArithmeticException.class, "算术异常");
        messages.put(ClassCastException.class, "类型转换异常");
        messages.put(NumberFormatException.class, "数字格式异常");
        messages.put(IllegalArgumentException.class, "非法参数");
        messages.put(ConcurrentModificationException.class, "并发修改异常");
        messages.put(HttpMessageNotReadableException.class, "HTT请求消息不可读");
        messages.put(FileNotFoundException.class, "未找到文件");
        messages.put(UnknownHostException.class, "未知主机异常");
        messages.put(BindException.class, "绑定异常(可能存在端口被占用)");
        messages.put(SocketException.class, "套接字异常");
        messages.put(ZipException.class, "压缩异常(文件可能已损坏)");
        messages.put(IOException.class, "I/O异常");
        messages.put(SQLException.class, "数据库操作异常");
        messages.put(ParseException.class, "数据解析异常");
        messages.put(InterruptedException.class, "线程中断异常");
        messages.put(NoSuchElementException.class, "元素不存在异常");
        messages.put(ClassNotFoundException.class, "类未找到异常");
        messages.put(IndexOutOfBoundsException.class, "非数组索引越界异常");
        messages.put(IllegalStateException.class, "非法状态异常");
        messages.put(UnsupportedOperationException.class, "不支持的操作异常");
        messages.put(SecurityException.class, "安全异常");
        messages.put(IllegalAccessException.class, "非法访问异常");
        messages.put(InstantiationException.class, "实例化异常");
        messages.put(TimeoutException.class, "超时异常");
        messages.put(ExecutionException.class, "执行异常(可能是Future的get方法抛出异常)");
        messages.put(UnsatisfiedDependencyException.class, "Spring依赖注入失败异常");
        messages.put(BeanCreationException.class, "Spring Bean创建异常");

        EXCEPTION_MESSAGES = messages;
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = Exception.class)
    public String exceptionHandler(HttpServletResponse response, Exception error) {
        String message = EXCEPTION_MESSAGES.getOrDefault(error.getClass(), "未知异常：" + error.getMessage());
        log.error(message, error);
        return message;
    }
}
